#include "GLDepthStencilState.h"

#include <Rz/Graphics/Render/Libraries/OpenGL/GLRenderDevice.h>

namespace Rz { namespace Graphics { namespace Render { namespace Libraries { namespace OpenGL {

GLDepthStencilState::GLDepthStencilState(IRenderDevice* renderDevice)
{
	this->SetRenderDevice(renderDevice);
}

void GLDepthStencilState::Bind()
{
	IDepthStencilState::Bind();

	// Depth
	if (this->GetDepthBufferEnable())
	{
		GL::Enable(GL_DEPTH_TEST);
	}
	else
	{
		GL::Disable(GL_DEPTH_TEST);
	}

	GL::DepthFunc(TranslateCompareFunction(this->GetDepthBufferFunction()));
	GL::DepthMask(this->GetDepthBufferWriteEnable());

	// Stencil
	if (this->GetStencilEnable())
	{
		GL::Enable(GL_STENCIL_TEST);
	}
	else
	{
		GL::Disable(GL_STENCIL_TEST);
	}

	GL::StencilMask(this->GetStencilWriteMask());

	if (this->GetTwoSidedStencilMode())
	{
		GL::StencilOpSeparate(GL_FRONT,
			TranslateStencilOp(this->GetStencilFail()),
			TranslateStencilOp(this->GetStencilDepthBufferFail()),
			TranslateStencilOp(this->GetStencilPass()));

		GL::StencilOpSeparate(GL_BACK,
			TranslateStencilOp(this->GetCounterClockwiseStencilFail()),
			TranslateStencilOp(this->GetCounterClockwiseStencilDepthBufferFail()),
			TranslateStencilOp(this->GetCounterClockwiseStencilPass()));

		GL::StencilFuncSeparate(GL_FRONT,
			TranslateCompareFunction(this->GetStencilFunction()),
			this->GetReferenceStencil(),
			this->GetStencilMask());

		GL::StencilFuncSeparate(GL_BACK,
			TranslateCompareFunction(this->GetCounterClockwiseStencilFunction()),
			this->GetReferenceStencil(),
			this->GetStencilMask());
	}
	else
	{
		GL::StencilOp(
			TranslateStencilOp(this->GetStencilFail()),
			TranslateStencilOp(this->GetStencilDepthBufferFail()),
			TranslateStencilOp(this->GetStencilPass()));

		GL::StencilFunc(
			TranslateCompareFunction(this->GetStencilFunction()),
			this->GetReferenceStencil(),
			this->GetStencilMask());
	}
}

void GLDepthStencilState::Unbind()
{
	IDepthStencilState::Unbind();
	//GL::BindSampler(0);
}

GLenum GLDepthStencilState::TranslateCompareFunction(CompareFunction cmpFn)
{
	switch (cmpFn)
	{
		default:
		case CompareFunction::Always:		return GL_ALWAYS;
		case CompareFunction::Equal:		return GL_EQUAL;
		case CompareFunction::Greater:		return GL_GREATER;
		case CompareFunction::GreaterEqual: return GL_GEQUAL;
		case CompareFunction::Less:			return GL_LESS;
		case CompareFunction::LessEqual:	return GL_LEQUAL;
		case CompareFunction::Never:		return GL_NEVER;
		case CompareFunction::NotEqual:		return GL_NOTEQUAL;
	}
}

GLenum GLDepthStencilState::TranslateStencilOp(StencilOperation operation)
{
	switch (operation)
	{
		default:
		case StencilOperation::Decrement:			return GL_DECR;
		case StencilOperation::DecrementSaturation:	return GL_DECR_WRAP;
		case StencilOperation::Increment:			return GL_INCR;
		case StencilOperation::IncrementSaturation:	return GL_INCR_WRAP;
		case StencilOperation::Invert:				return GL_INVERT;
		case StencilOperation::Keep:				return GL_KEEP;
		case StencilOperation::Replace:				return GL_REPLACE;
		case StencilOperation::Zero:				return GL_ZERO;
	}
}

} } } } }